namespace Org.Loon.Framework.Xna.Game.Core.Graphics.OpenGL.Particle
{
    using System.Runtime.CompilerServices;
    using Org.Loon.Framework.Xna.Game.Utils;
    using Org.Loon.Framework.Xna.Java;

    public class Emitter : LRelease
    {

        public class ExplosionEmitter : Emitter
        {

            private bool didExplode;

            private float x, y, scale, scaleRate;

            private float updateRate, alphaRate, velY, velX, accX, accY;

            private int nowParticleCount;

            private float pMinVelX;
            private float pMaxVelX;
            private float pMinVelY;
            private float pMaxVelY;
            private float pMinScale;
            private float pMaxScale;
            private float pMinAlphaRate;
            private float pMaxAlphaRate;
            private float pMinScaleRate;
            private float pMaxScaleRate;
            private float pMinAccX;
            private float pMaxAccX;
            private float pMinAccY;
            private float pMaxAccY;

            public ExplosionEmitter(float x, float y, LTexture texture,
                    int particleCount, float minVelX, float maxVelX, float minVelY,
                    float maxVelY, float minAccX, float maxAccX, float minAccY,
                    float maxAccY, float minScale, float maxScale,
                    float minShrinkRate, float maxShrinkRate, float minAlphaRate,
                    float maxAlphaRate)
                : base(x, y, 1, texture)
            {

                didExplode = false;
                nowParticleCount = particleCount;
                pMinVelX = minVelX;
                pMaxVelX = maxVelX;
                pMinVelY = minVelY;
                pMaxVelY = maxVelY;
                pMinScale = minScale;
                pMaxScale = maxScale;
                pMinAlphaRate = minAlphaRate;
                pMaxAlphaRate = maxAlphaRate;
                pMinScaleRate = minShrinkRate;
                pMaxScaleRate = maxShrinkRate;
                pMinAccX = minAccX;
                pMaxAccX = maxAccX;
                pMinAccY = minAccY;
                pMaxAccY = maxAccY;
            }

            public void UpdateSpawns()
            {
                if (!didExplode)
                {
                    didExplode = true;
                    scaleRate = pMinScale + ((pMaxScale - pMinScale) / 2);
                    for (int i = 0; i < nowParticleCount; i++)
                    {
                        scale = pMinScale
                                + (MathUtils.Random() * (pMaxScale - pMinScale));
                        x = GetX() - (scaleRate / 2);
                        y = GetY() - (scaleRate / 2);
                        updateRate = pMinScaleRate
                                + (MathUtils.Random() * (pMaxScaleRate - pMinScaleRate));
                        alphaRate = pMinAlphaRate
                                + (MathUtils.Random() * (pMaxAlphaRate - pMinAlphaRate));
                        velY = pMinVelX
                                + (MathUtils.Random() * (pMaxVelX - pMinVelX));
                        velX = pMinVelY
                                + (MathUtils.Random() * (pMaxVelY - pMinVelY));
                        accX = pMinAccX
                                + (MathUtils.Random() * (pMaxAccX - pMinAccX));
                        accY = pMinAccY
                                + (MathUtils.Random() * (pMaxAccY - pMinAccY));
                        SetNode(new Node(this, 0, x, y, 1, scale, alphaRate,
                                updateRate, velX, velY, accX, accY));
                    }
                }
                if (IsEmpty())
                {
                    Delete(true);
                }
            }
        }

        public class BasicEmitter : Emitter
        {

            private float x, y, scale, scaleRate;

            private float alphaRate, velY, velX, accX, accY;

            private float pMinVelX;

            private float pMaxVelX;

            private float pMinVelY;

            private float pMaxVelY;

            private float pMinScale;

            private float pMaxScale;

            private float pMinAlphaRate;

            private float pMaxAlphaRate;

            private float pMinShrinkRate;

            private float pMaxShrinkRate;

            private float pMinAccX;

            private float pMaxAccX;

            private float pMinAccY;

            private float pMaxAccY;

            private long pLife;

            public BasicEmitter(float x, float y, float rate, LTexture texture,
                    long life, float minVelX, float maxVelX, float minVelY,
                    float maxVelY, float minAccX, float maxAccX, float minAccY,
                    float maxAccY, float minScale, float maxScale,
                    float minShrinkRate, float maxShrinkRate, float minAlphaRate,
                    float maxAlphaRate)
                : base(x, y, rate, texture)
            {

                pMinVelX = minVelX;
                pMaxVelX = maxVelX;
                pMinVelY = minVelY;
                pMaxVelY = maxVelY;
                pMinScale = minScale;
                pMaxScale = maxScale;
                pMinAlphaRate = minAlphaRate;
                pMaxAlphaRate = maxAlphaRate;
                pMinShrinkRate = minShrinkRate;
                pMaxShrinkRate = maxShrinkRate;
                pMinAccX = minAccX;
                pMaxAccX = maxAccX;
                pMinAccY = minAccY;
                pMaxAccY = maxAccY;
                pLife = life;
            }

            public override void Update()
            {
                scale = pMinScale + (MathUtils.Random() * (pMaxScale - pMinScale));
                x = GetX() - (scale / 2);
                y = GetY() - (scale / 2);
                scaleRate = pMinShrinkRate
                        + (MathUtils.Random() * (pMaxShrinkRate - pMinShrinkRate));
                alphaRate = pMinAlphaRate
                        + (MathUtils.Random() * (pMaxAlphaRate - pMinAlphaRate));
                velX = pMinVelX + (MathUtils.Random() * (pMaxVelX - pMinVelX));
                velY = pMinVelY + (MathUtils.Random() * (pMaxVelY - pMinVelY));
                accX = pMinAccX + (MathUtils.Random() * (pMaxAccX - pMinAccX));
                accY = pMinAccY + (MathUtils.Random() * (pMaxAccY - pMinAccY));
                SetNode(new Node(this, pLife, x, y, 1, scale, alphaRate, scaleRate,
                        velX, velY, accX, accY));
            }

        }

        public class Node
        {

            public bool kill;

            public float x, y;

            public float scale, alpha;

            public float alphaSleep, scaleSleep;

            public float accelerationX;

            public float accelerationY;

            public float velocityX;

            public float velocityY;

            public float red;

            public float green;

            public float blue;

            public long timeout;

            private long lastUpdate;

            public Node(Emitter emitter, long life, float pX, float pY,
                    float pAlpha, float pScale, float alphaSpeed, float scaleSleep,
                    float pVelocityX, float pVelocityY, float pAccelerationX,
                    float pAccelerationY)
            {
                this.x = pX;
                this.y = pY;
                this.alpha = pAlpha;
                this.scale = pScale;
                this.alphaSleep = alphaSpeed;
                this.velocityX = pVelocityX;
                this.velocityY = pVelocityY;
                this.accelerationX = pAccelerationX;
                this.accelerationY = pAccelerationY;
                this.scaleSleep = scaleSleep;
                if (life > 0)
                {
                    this.timeout = JavaRuntime.CurrentTimeMillis() + life;
                }
                else
                {
                    this.timeout = 0;
                }
                this.red = 1;
                this.green = 1;
                this.blue = 1;
                this.lastUpdate = JavaRuntime.CurrentTimeMillis();
            }

            public void Run()
            {
                long now = JavaRuntime.CurrentTimeMillis();
                long timeDiff = now - lastUpdate;
                float modifier = (float)timeDiff / 1000f;
                alpha -= alphaSleep * modifier;
                scale -= scaleSleep * modifier;
                if (alpha <= 0 || scale <= 0 || (now > timeout && timeout > 0))
                {
                    alpha = 0;
                    scale = 0;
                    kill = true;
                    return;
                }
                velocityX += accelerationX * modifier;
                velocityY += accelerationY * modifier;
                x += (velocityX * modifier) + ((scaleSleep * modifier) / 2);
                y += (velocityY * modifier) + ((scaleSleep * modifier) / 2);
                lastUpdate = now;
            }
        }

        private LColor color = new LColor(LColor.white);

        private Node[] nodes = new Node[50];

        private bool pKill = false;

        private long now, timeDiff;

        private int count;

        private float pX;

        private float pY;

        private float rate;

        private long lastUpdate;

        private LTextureBatch batch;

        private LTexture pTexture;

        private int pIndex, pCount;

        public Emitter(float x, float y, float rate, string fileName)
            : this(x, y, rate, LTextures.LoadTexture(fileName))
        {

        }

        public Emitter(float x, float y, float r, LTexture tex2d)
        {
            this.pX = x;
            this.pY = y;
            this.rate = (1 / r) * 1000;
            this.pTexture = tex2d;
            this.lastUpdate = JavaRuntime.CurrentTimeMillis();
            this.batch = new LTextureBatch(pTexture);
        }

        public void Delete(bool mark)
        {
            pKill = mark;
        }

        public bool IsKill()
        {
            return pKill;
        }

        public virtual void Update()
        {

        }

        public void SetNode(Node n)
        {
            pIndex = -1;
            for (int i = 0; i < 50; i++)
            {
                if (nodes[i] == null)
                {
                    pIndex = i;
                }
            }
            if (pIndex == -1)
            {
                return;
            }
            nodes[pIndex] = n;
        }

        public void UpdateNodes()
        {
            now = JavaRuntime.CurrentTimeMillis();
            timeDiff = now - lastUpdate;
            count = MathUtils.Round(timeDiff / rate);
            if (count > 0)
            {
                for (int i = 0; i < count; i++)
                {
                    Update();
                }
                lastUpdate = now;
            }
        }

        public void Draw()
        {
            UpdateNodes();
            batch.GLBegin();
            for (int i = 0; i < 50; i++)
            {
                if (nodes[i] != null)
                {
                    nodes[i].Run();
                    if (nodes[i].kill)
                    {
                        nodes[i] = null;
                    }
                    else
                    {
                        color.SetColor(nodes[i].red, nodes[i].green, nodes[i].blue,
                                nodes[i].alpha);
                        batch.Draw(nodes[i].x, nodes[i].y, pTexture.GetWidth()
                                * nodes[i].scale, pTexture.GetHeight()
                                * nodes[i].scale, color);
                    }
                }
            }
            batch.GLEnd();
        }

        public void SetLocation(float x, float y)
        {
            pX = x;
            pY = y;
        }

        public float GetX()
        {
            return pX;
        }

        public float GetY()
        {
            return pY;
        }

        public int NodeCount()
        {
            pCount = 0;
            for (int i = 0; i < 50; i++)
            {
                if (nodes[i] != null)
                {
                    pCount++;
                }
            }
            return pCount;
        }

        public bool IsEmpty()
        {
            for (int i = 0; i < 50; i++)
            {
                if (nodes[i] != null)
                {
                    return false;
                }
            }
            return true;
        }

        public void Dispose()
        {
            if (batch != null)
            {
                batch.Dispose();
            }
            if (pTexture != null)
            {
                pTexture.Dispose();
            }
        }
    }
}
